package cn.cosmosx.base.encrypt.loader;

import cn.cosmosx.base.encrypt.sm.Provider;
import cn.cosmosx.base.exception.PlatformException;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

/**
 * PK12证书加载器
 */
@Slf4j
public final class PFXCertFileLoader {
    private final static Charset CHARSET = StandardCharsets.UTF_8;
    private static final BouncyCastleProvider PROVIDER = Provider.INSTANCE.getProvider();

    /**
     * PFX（pk12）证书加载拿到公私钥
     *
     * @param pfxFilePath - 证书路径
     * @param password    - 证书访问密码
     * @param alias       - 证书别名
     */
    public static Map<String, String> loadKeyPairFromPKCS12(String pfxFilePath, String password, String alias) {
        try {
            Map<String, String> keyPair = new HashMap<>(2);
            KeyStore jks = loadKeyStore(pfxFilePath, password, alias);
            // 根据密码，获取私钥
            BCECPrivateKey privateKey = (BCECPrivateKey) jks.getKey(alias, password.toCharArray());
            String priKey = Hex.toHexString(privateKey.getD().toByteArray());
            log.info("私钥：" + priKey);
            // 根据证书，获取公钥
            BCECPublicKey publicKey = (BCECPublicKey) jks.getCertificate(alias).getPublicKey();
            String pubKey = Hex.toHexString(publicKey.getQ().getEncoded(false));
            log.info("公钥（非压缩）：" + pubKey);
            keyPair.put("pubKey", pubKey);
            keyPair.put("priKey", priKey);
            return keyPair;
        } catch (Exception e) {
            log.error("证书加载错误", e);
            throw new PlatformException("加载证书文件出错！");
        }
    }

    /**
     * 加载私钥
     *
     * @param pfxFilePath - 证书路径
     * @param password    - 证书访问密码
     * @param alias       - 证书别名
     * @return - BCECPrivateKey
     */
    public static String loadPrivateKeyHex(String pfxFilePath, String password, String alias) {
        return Hex.toHexString(loadPrivateKey(pfxFilePath, password, alias).getD().toByteArray());
    }

    /**
     * 加载公钥（非压缩）
     *
     * @param pfxFilePath - 证书路径
     * @param password    - 证书访问密码
     * @param alias       - 证书别名
     * @return - BCECPrivateKey
     */
    public static String loadPublicKeyHex(String pfxFilePath, String password, String alias) {
        return loadPublicKeyHex(pfxFilePath, password, alias, false);
    }

    /**
     * 加载公钥
     *
     * @param pfxFilePath - 证书路径
     * @param password    - 证书访问密码
     * @param alias       - 证书别名
     * @param compressed  - 证书公钥是否为压缩
     * @return - BCECPrivateKey
     */
    public static String loadPublicKeyHex(String pfxFilePath, String password, String alias, boolean compressed) {
        return Hex.toHexString(loadPublicKey(pfxFilePath, password, alias).getQ().getEncoded(compressed));
    }

    /**
     * 加载私钥
     *
     * @param pfxFilePath - 证书路径
     * @param password    - 证书访问密码
     * @param alias       - 证书别名
     * @return - BCECPrivateKey
     */
    public static BCECPrivateKey loadPrivateKey(String pfxFilePath, String password, String alias) {
        try {
            KeyStore jks = loadKeyStore(pfxFilePath, password, alias);
            // 根据密码，获取私钥
            return (BCECPrivateKey) jks.getKey(alias, password.toCharArray());
        } catch (Exception e) {
            log.error("证书加载错误", e);
            throw new PlatformException("加载证书文件出错！");
        }
    }


    /**
     * 加载公钥
     *
     * @param pfxFilePath - 证书路径
     * @param password    - 证书访问密码
     * @param alias       - 证书别名
     * @return - BCECPrivateKey
     */
    public static BCECPublicKey loadPublicKey(String pfxFilePath, String password, String alias) {
        try {
            KeyStore jks = loadKeyStore(pfxFilePath, password, alias);
            // 根据密码，获取私钥
            return (BCECPublicKey) jks.getCertificate(alias).getPublicKey();
        } catch (Exception e) {
            log.error("证书加载错误", e);
            throw new PlatformException("加载证书文件出错！");
        }
    }

    /**
     * 加载 KeyStore
     *
     * @param pfxFilePath - 证书路径
     * @param password    - 证书访问密码
     * @param alias       - 证书别名
     * @return - KeyStore
     */
    public static KeyStore loadKeyStore(String pfxFilePath, String password, String alias) {
        try (InputStream is = loadFile(pfxFilePath)) {
            KeyStore jks = KeyStore.getInstance("pkcs12", PROVIDER);
            jks.load(is, password.toCharArray());
            Certificate certificate = jks.getCertificate(alias);
            if (certificate == null) {
                throw new PlatformException("无效的证书信息");
            }
            if (!(certificate instanceof X509Certificate)) {
                throw new PlatformException("不支持X.509格式的证书");
            }
            return jks;
        } catch (Exception e) {
            log.error("证书加载错误", e);
            throw new PlatformException("加载证书文件出错！");
        }
    }

    private static InputStream loadFile(String pfxFilePath) throws FileNotFoundException {
        InputStream inputStream = PFXCertFileLoader.class.getResourceAsStream(pfxFilePath);
        if (inputStream == null) {
            inputStream = new FileInputStream(pfxFilePath);
        }
        return inputStream;
    }

}
